/*****************************************************
** Author:
** Date: 2019-07-25
*
*****************************************************/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>
#include <bcm2835.h>
#include "spi_if.h"
#include "tm7705.h"

#define log_dbg(_fmt_, ...)   \
            printf("[APP][%s:%d][%s][DBG] "_fmt_"\r\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define log_inf(_fmt_, ...)   \
            printf("[APP][%s:%d][%s][INF] "_fmt_"\r\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define log_wrn(_fmt_, ...)   \
            printf("[APP][%s:%d][%s][WRN] "_fmt_"\r\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define log_err(_fmt_, ...)   \
            printf("[APP][%s:%d][%s][ERR] "_fmt_"\r\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)

#define TM7705_PIN_CS   22
#define TM7705_PIN_DRDY 17
#define TM7705_PIN_RST  27

#define JOYSTICK_X_CHAN 1
#define JOYSTICK_Y_CHAN 0

#define SAMPLE_ARRAY_SIZE 8
#define SAMPLE_AVG_OFFSET 3
#define SAMPLE_POS_MASK   0x07

typedef struct sum_avg_filer {
    unsigned short buff[SAMPLE_ARRAY_SIZE];
    unsigned short avg;
    unsigned int sum;
    unsigned int pos;
    unsigned int ch;
} sum_avg_filer_t;

/**
 * @brief
 * @param  None
 * @retval None
 */
static void tm7705_gpio_init(void)
{
    bcm2835_gpio_fsel(TM7705_PIN_CS, BCM2835_GPIO_FSEL_OUTP);
    bcm2835_gpio_write(TM7705_PIN_CS, HIGH);

    bcm2835_gpio_fsel(TM7705_PIN_DRDY, BCM2835_GPIO_FSEL_INPT);
    bcm2835_gpio_set_pud(TM7705_PIN_DRDY, BCM2835_GPIO_PUD_UP);

    bcm2835_gpio_fsel(TM7705_PIN_RST, BCM2835_GPIO_FSEL_OUTP);
    bcm2835_gpio_write(TM7705_PIN_RST, HIGH);
}

static unsigned char tm7705_spi_transfer(unsigned char val)
{
    return spi_transfer(val);
}

static void tm7705_spi_csn(unsigned char level)
{
    bcm2835_gpio_write(TM7705_PIN_CS, level);
    // log_dbg("set cs %d", level);
}

static void tm7705_rst(unsigned char level)
{
    bcm2835_gpio_write(TM7705_PIN_RST, level);
}

static unsigned char tm7705_drdy_level(void)
{
    return bcm2835_gpio_lev(TM7705_PIN_DRDY);
}

static void plat_delay_ms(unsigned int ms)
{
    bcm2835_delay(ms);
}

static void plat_delay_us(unsigned int us)
{
    bcm2835_delayMicroseconds(us);
}

static void tm7705_hexdump(char *str, unsigned char *data, unsigned int len)
{
    printf("hexdump %p len %d %s", data, len, str);
    for (int i = 0; i < len; i++) {
        if (!(i & 0xf))
            printf("\n%p:", &data[i]);
        printf(" %02x", data[i]);
    }
    printf("\n");
}

static int tm7705_dev_init(tm7705_obj_t *obj)
{
    tm7705_gpio_init();

    if (spi_init(BCM2835_SPI_BIT_ORDER_MSBFIRST,
                 BCM2835_SPI_MODE3,
                 BCM2835_SPI_CLOCK_DIVIDER_1024) != 0) {
        log_err("spi init failed");
        return -1;
    }

    obj->spi_transfer_byte = tm7705_spi_transfer;
    obj->spi_csn_level = tm7705_spi_csn;
    obj->reset_level = tm7705_rst;
    obj->drdy_level = tm7705_drdy_level;
    obj->delay_ms = plat_delay_ms;
    obj->delay_us = plat_delay_us;

    tm7705_rst(0);
    plat_delay_ms(1);
    tm7705_rst(1);

    tm7705_spi_csn(0);
    tm7705_spi_transfer(0xff);
    tm7705_spi_transfer(0xff);
    tm7705_spi_transfer(0xff);
    tm7705_spi_transfer(0xff);
    tm7705_spi_transfer(0xff);
    tm7705_spi_csn(1);

    tm7705_set_clk(obj);
    tm7705_self_cali(obj);

    while (tm7705_read_drdy(obj)) {
        plat_delay_ms(10);
    }

    return 0;
}

static void exit_handler(int signo)
{
    //System Exit
    log_inf("Program Stop");

    // tm7705_deinit();
    spi_deinit();
    bcm2835_close();

    exit(0);
}

int sum_avg_filter_init(tm7705_obj_t *obj, sum_avg_filer_t *tar)
{
    unsigned short val;
    int ret, i;

    for (i = 0; i < SAMPLE_ARRAY_SIZE; ++i) {
        if ((ret = tm7705_read_adc(obj, tar->ch, &val)) != 0) {
            return -1;
        }
        tar->buff[tar->pos] = val;
        tar->sum += val;
        tar->pos = (++tar->pos) & SAMPLE_POS_MASK;
    }
    tar->avg = tar->sum >> SAMPLE_AVG_OFFSET;
    return 0;
}

int sum_avg_filter_update(tm7705_obj_t *obj, sum_avg_filer_t *tar)
{
    unsigned short val;
    int ret;

    if ((ret = tm7705_read_adc(obj, tar->ch, &val)) != 0) {
        return -1;
    }
    tar->sum -= tar->buff[tar->pos];
    tar->buff[tar->pos] = val;
    tar->sum += tar->buff[tar->pos];
    tar->pos = (++tar->pos) & SAMPLE_POS_MASK;
    tar->avg = tar->sum >> SAMPLE_AVG_OFFSET;
    return 0;
}

int main(int argc, char const *argv[])
{
    static tm7705_obj_t tm7705;
    struct timeval tv1, tv2;
    sum_avg_filer_t x, y;
    int ret;

    if (!bcm2835_init()) {
        log_err("bcm2835_init failed. Are you running as root??\n");
        return -1;
    }

    if (tm7705_dev_init(&tm7705) < 0) {
        log_err("tm7705 init failed");
        return -1;
    }

    // Exception handling:ctrl + c
    signal(SIGINT, exit_handler);

    log_inf("init done");

    memset(&x, 0, sizeof(sum_avg_filer_t));
    memset(&y, 0, sizeof(sum_avg_filer_t));

    x.ch = JOYSTICK_X_CHAN;
    y.ch = JOYSTICK_Y_CHAN;

    if ((ret = sum_avg_filter_init(&tm7705, &x)) != 0) {
        log_wrn("read x failed %d", ret);
        goto err;
    }

    // if ((ret = sum_avg_filter_init(&tm7705, &y)) != 0) {
    //     log_wrn("read y failed %d", ret);
    //     goto err;
    // }

    while (1) {

        gettimeofday(&tv1, NULL);

        if ((ret = sum_avg_filter_update(&tm7705, &x)) != 0) {
            log_wrn("read x failed %d", ret);
            goto err;
        }

        // if ((ret = sum_avg_filter_update(&tm7705, &y)) != 0) {
        //     log_wrn("read y failed %d", ret);
        //     goto err;
        // }

        gettimeofday(&tv2, NULL);

        log_dbg("read adc x %#x y %#x delat time %dus", x.avg, y.avg,
            (tv2.tv_sec - tv1.tv_sec) * 1000000 + tv2.tv_usec - tv1.tv_usec);
    }

err:
    exit_handler(0);
    return 0;
}

/* End of file */
